#include <ctype.h>
#include <stddef.h>

#include "tinyxml.h"

#if defined( DEBUG_PARSER )
# if defined( DEBUG ) && defined( _MSC_VER )
#   include <windows.h>
#   define TIXML_LOG OutputDebugString
# else
#   define TIXML_LOG printf
# endif
#endif


TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = {
  { "&amp;",  5, '&' },
  { "&lt;",   4, '<' },
  { "&gt;",   4, '>' },
  { "&quot;", 6, '\"' },
  { "&apos;", 6, '\'' }
};




const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;

const int TiXmlBase::utf8ByteTable[256] = {

  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
  4,  4,  4,  4,  4,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1
};


void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) {
  const unsigned long BYTE_MASK = 0xBF;
  const unsigned long BYTE_MARK = 0x80;
  const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  if( input < 0x80 ) {
    *length = 1;
  } else if( input < 0x800 ) {
    *length = 2;
  } else if( input < 0x10000 ) {
    *length = 3;
  } else if( input < 0x200000 ) {
    *length = 4;
  } else {
    *length = 0;
    return;
  }
  output += *length;
  switch( *length ) {
    case 4:
      --output;
      *output = ( char )( ( input | BYTE_MARK ) & BYTE_MASK );
      input >>= 6;
    case 3:
      --output;
      *output = ( char )( ( input | BYTE_MARK ) & BYTE_MASK );
      input >>= 6;
    case 2:
      --output;
      *output = ( char )( ( input | BYTE_MARK ) & BYTE_MASK );
      input >>= 6;
    case 1:
      --output;
      *output = ( char )( input | FIRST_BYTE_MARK[*length] );
  }
}


int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding ) {
  if( anyByte < 127 ) {
    return isalpha( anyByte );
  } else {
    return 1;
  }
}


int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding ) {
  if( anyByte < 127 ) {
    return isalnum( anyByte );
  } else {
    return 1;
  }
}


class TiXmlParsingData {
    friend class TiXmlDocument;
  public:
    void Stamp( const char* now, TiXmlEncoding encoding );

    const TiXmlCursor& Cursor() { return cursor; }

  private:

    TiXmlParsingData( const char* start, int _tabsize, int row, int col ) {
      assert( start );
      stamp = start;
      tabsize = _tabsize;
      cursor.row = row;
      cursor.col = col;
    }

    TiXmlCursor   cursor;
    const char*   stamp;
    int       tabsize;
};


void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) {
  assert( now );
  if( tabsize < 1 ) {
    return;
  }
  int row = cursor.row;
  int col = cursor.col;
  const char* p = stamp;
  assert( p );
  while( p < now ) {
    const unsigned char* pU = ( const unsigned char* )p;
    switch( *pU ) {
      case 0:
        return;
      case '\r':
        ++row;
        col = 0;
        ++p;
        if( *p == '\n' ) {
          ++p;
        }
        break;
      case '\n':
        ++row;
        col = 0;
        ++p;
        if( *p == '\r' ) {
          ++p;
        }
        break;
      case '\t':
        ++p;
        col = ( col / tabsize + 1 ) * tabsize;
        break;
      case TIXML_UTF_LEAD_0:
        if( encoding == TIXML_ENCODING_UTF8 ) {
          if( *( p + 1 ) && *( p + 2 ) ) {
            if( *( pU + 1 ) == TIXML_UTF_LEAD_1 && *( pU + 2 ) == TIXML_UTF_LEAD_2 )
            { p += 3; }
            else if( *( pU + 1 ) == 0xbfU && *( pU + 2 ) == 0xbeU )
            { p += 3; }
            else if( *( pU + 1 ) == 0xbfU && *( pU + 2 ) == 0xbfU )
            { p += 3; }
            else
            { p += 3; ++col; }
          }
        } else {
          ++p;
          ++col;
        }
        break;
      default:
        if( encoding == TIXML_ENCODING_UTF8 ) {
          int step = TiXmlBase::utf8ByteTable[*( ( const unsigned char* )p )];
          if( step == 0 ) {
            step = 1;
          }
          p += step;
          ++col;
        } else {
          ++p;
          ++col;
        }
        break;
    }
  }
  cursor.row = row;
  cursor.col = col;
  assert( cursor.row >= -1 );
  assert( cursor.col >= -1 );
  stamp = p;
  assert( stamp );
}


const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) {
  if( !p || !*p ) {
    return 0;
  }
  if( encoding == TIXML_ENCODING_UTF8 ) {
    while( *p ) {
      const unsigned char* pU = ( const unsigned char* )p;
      if( *( pU + 0 ) == TIXML_UTF_LEAD_0
          && *( pU + 1 ) == TIXML_UTF_LEAD_1
          && *( pU + 2 ) == TIXML_UTF_LEAD_2 ) {
        p += 3;
        continue;
      } else if( *( pU + 0 ) == TIXML_UTF_LEAD_0
                 && *( pU + 1 ) == 0xbfU
                 && *( pU + 2 ) == 0xbeU ) {
        p += 3;
        continue;
      } else if( *( pU + 0 ) == TIXML_UTF_LEAD_0
                 && *( pU + 1 ) == 0xbfU
                 && *( pU + 2 ) == 0xbfU ) {
        p += 3;
        continue;
      }
      if( IsWhiteSpace( *p ) ) {
        ++p;
      } else
      { break; }
    }
  } else {
    while( *p && IsWhiteSpace( *p ) ) {
      ++p;
    }
  }
  return p;
}

#ifdef TIXML_USE_STL
bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) {
  for( ;; ) {
    if( !in->good() ) {
      return false;
    }
    int c = in->peek();
    if( !IsWhiteSpace( c ) || c <= 0 ) {
      return true;
    }
    *tag += ( char ) in->get();
  }
}

bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) {
  while( in->good() ) {
    int c = in->peek();
    if( c == character ) {
      return true;
    }
    if( c <= 0 ) {
      return false;
    }
    in->get();
    *tag += ( char ) c;
  }
  return false;
}
#endif


const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) {
  *name = "";
  assert( p );
  if( p && *p
      && ( IsAlpha( ( unsigned char ) *p, encoding ) || *p == '_' ) ) {
    const char* start = p;
    while( p && *p
           && ( IsAlphaNum( ( unsigned char ) *p, encoding )
                || *p == '_'
                || *p == '-'
                || *p == '.'
                || *p == ':' ) ) {
      ++p;
    }
    if( p - start > 0 ) {
      name->assign( start, p - start );
    }
    return p;
  }
  return 0;
}

const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) {
  TIXML_STRING ent;
  int i;
  *length = 0;
  if( *( p + 1 ) && *( p + 1 ) == '#' && *( p + 2 ) ) {
    unsigned long ucs = 0;
    ptrdiff_t delta = 0;
    unsigned mult = 1;
    if( *( p + 2 ) == 'x' ) {
      if( !*( p + 3 ) ) {
        return 0;
      }
      const char* q = p + 3;
      q = strchr( q, ';' );
      if( !q || !*q ) {
        return 0;
      }
      delta = q - p;
      --q;
      while( *q != 'x' ) {
        if( *q >= '0' && *q <= '9' ) {
          ucs += mult * ( *q - '0' );
        } else if( *q >= 'a' && *q <= 'f' ) {
          ucs += mult * ( *q - 'a' + 10 );
        } else if( *q >= 'A' && *q <= 'F' ) {
          ucs += mult * ( *q - 'A' + 10 );
        } else
        { return 0; }
        mult *= 16;
        --q;
      }
    } else {
      if( !*( p + 2 ) ) {
        return 0;
      }
      const char* q = p + 2;
      q = strchr( q, ';' );
      if( !q || !*q ) {
        return 0;
      }
      delta = q - p;
      --q;
      while( *q != '#' ) {
        if( *q >= '0' && *q <= '9' ) {
          ucs += mult * ( *q - '0' );
        } else
        { return 0; }
        mult *= 10;
        --q;
      }
    }
    if( encoding == TIXML_ENCODING_UTF8 ) {
      ConvertUTF32ToUTF8( ucs, value, length );
    } else {
      *value = ( char )ucs;
      *length = 1;
    }
    return p + delta + 1;
  }
  for( i = 0; i < NUM_ENTITY; ++i ) {
    if( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) {
      assert( strlen( entity[i].str ) == entity[i].strLength );
      *value = entity[i].chr;
      *length = 1;
      return ( p + entity[i].strLength );
    }
  }
  *value = *p;
  return p + 1;
}


bool TiXmlBase::StringEqual( const char* p,
                             const char* tag,
                             bool ignoreCase,
                             TiXmlEncoding encoding ) {
  assert( p );
  assert( tag );
  if( !p || !*p ) {
    assert( 0 );
    return false;
  }
  const char* q = p;
  if( ignoreCase ) {
    while( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) {
      ++q;
      ++tag;
    }
    if( *tag == 0 ) {
      return true;
    }
  } else {
    while( *q && *tag && *q == *tag ) {
      ++q;
      ++tag;
    }
    if( *tag == 0 )    // Have we found the end of the tag, and everything equal?
    { return true; }
  }
  return false;
}

const char* TiXmlBase::ReadText( const char* p,
                                 TIXML_STRING * text,
                                 bool trimWhiteSpace,
                                 const char* endTag,
                                 bool caseInsensitive,
                                 TiXmlEncoding encoding ) {
  *text = "";
  if( !trimWhiteSpace         // certain tags always keep whitespace
      || !condenseWhiteSpace ) { // if true, whitespace is always kept
    // Keep all the white space.
    while( p && *p
           && !StringEqual( p, endTag, caseInsensitive, encoding )
         ) {
      int len;
      char cArr[4] = { 0, 0, 0, 0 };
      p = GetChar( p, cArr, &len, encoding );
      text->append( cArr, len );
    }
  } else {
    bool whitespace = false;
    // Remove leading white space:
    p = SkipWhiteSpace( p, encoding );
    while( p && *p
           && !StringEqual( p, endTag, caseInsensitive, encoding ) ) {
      if( *p == '\r' || *p == '\n' ) {
        whitespace = true;
        ++p;
      } else if( IsWhiteSpace( *p ) ) {
        whitespace = true;
        ++p;
      } else {
        // If we've found whitespace, add it before the
        // new character. Any whitespace just becomes a space.
        if( whitespace ) {
          ( *text ) += ' ';
          whitespace = false;
        }
        int len;
        char cArr[4] = { 0, 0, 0, 0 };
        p = GetChar( p, cArr, &len, encoding );
        if( len == 1 ) {
          ( *text ) += cArr[0];
        }
        // more efficient
        else
        { text->append( cArr, len ); }
      }
    }
  }
  if( p && *p ) {
    p += strlen( endTag );
  }
  return p;
}

#ifdef TIXML_USE_STL

void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  // The basic issue with a document is that we don't know what we're
  // streaming. Read something presumed to be a tag (and hope), then
  // identify it, and call the appropriate stream method on the tag.
  //
  // This "pre-streaming" will never read the closing ">" so the
  // sub-tag can orient itself.
  if( !StreamTo( in, '<', tag ) ) {
    SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
    return;
  }
  while( in->good() ) {
    int tagIndex = ( int ) tag->length();
    while( in->good() && in->peek() != '>' ) {
      int c = in->get();
      if( c <= 0 ) {
        SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
        break;
      }
      ( *tag ) += ( char ) c;
    }
    if( in->good() ) {
      // We now have something we presume to be a node of
      // some sort. Identify it, and call the node to
      // continue streaming.
      TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
      if( node ) {
        node->StreamIn( in, tag );
        bool isElement = node->ToElement() != 0;
        delete node;
        node = 0;
        // If this is the root element, we're done. Parsing will be
        // done by the >> operator.
        if( isElement ) {
          return;
        }
      } else {
        SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
        return;
      }
    }
  }
  // We should have returned sooner.
  SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
}

#endif

const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) {
  ClearError();
  // Parse away, at the document level. Since a document
  // contains nothing but other tags, most of what happens
  // here is skipping white space.
  if( !p || !*p ) {
    SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
    return 0;
  }
  // Note that, for a document, this needs to come
  // before the while space skip, so that parsing
  // starts from the pointer we are given.
  location.Clear();
  if( prevData ) {
    location.row = prevData->cursor.row;
    location.col = prevData->cursor.col;
  } else {
    location.row = 0;
    location.col = 0;
  }
  TiXmlParsingData data( p, TabSize(), location.row, location.col );
  location = data.Cursor();
  if( encoding == TIXML_ENCODING_UNKNOWN ) {
    // Check for the Microsoft UTF-8 lead bytes.
    const unsigned char* pU = ( const unsigned char* )p;
    if( *( pU + 0 ) && *( pU + 0 ) == TIXML_UTF_LEAD_0
        && *( pU + 1 ) && *( pU + 1 ) == TIXML_UTF_LEAD_1
        && *( pU + 2 ) && *( pU + 2 ) == TIXML_UTF_LEAD_2 ) {
      encoding = TIXML_ENCODING_UTF8;
      useMicrosoftBOM = true;
    }
  }
  p = SkipWhiteSpace( p, encoding );
  if( !p ) {
    SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
    return 0;
  }
  while( p && *p ) {
    TiXmlNode* node = Identify( p, encoding );
    if( node ) {
      p = node->Parse( p, &data, encoding );
      LinkEndChild( node );
    } else
    { break; }
    // Did we get encoding info?
    if( encoding == TIXML_ENCODING_UNKNOWN
        && node->ToDeclaration() ) {
      TiXmlDeclaration* dec = node->ToDeclaration();
      const char* enc = dec->Encoding();
      assert( enc );
      if( *enc == 0 ) {
        encoding = TIXML_ENCODING_UTF8;
      } else if( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) {
        encoding = TIXML_ENCODING_UTF8;
      } else if( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) {
        encoding = TIXML_ENCODING_UTF8;
      }
      // incorrect, but be nice
      else
      { encoding = TIXML_ENCODING_LEGACY; }
    }
    p = SkipWhiteSpace( p, encoding );
  }
  // Was this empty?
  if( !firstChild ) {
    SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
    return 0;
  }
  // All is well.
  return p;
}

void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  // The first error in a chain is more accurate - don't set again!
  if( error ) {
    return;
  }
  assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
  error   = true;
  errorId = err;
  errorDesc = errorString[ errorId ];
  errorLocation.Clear();
  if( pError && data ) {
    data->Stamp( pError, encoding );
    errorLocation = data->Cursor();
  }
}


TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) {
  TiXmlNode* returnNode = 0;
  p = SkipWhiteSpace( p, encoding );
  if( !p || !*p || *p != '<' ) {
    return 0;
  }
  p = SkipWhiteSpace( p, encoding );
  if( !p || !*p ) {
    return 0;
  }
  const char* xmlHeader = { "<?xml" };
  const char* commentHeader = { "<!--" };
  const char* dtdHeader = { "<!" };
  const char* cdataHeader = { "<![CDATA[" };
  if( StringEqual( p, xmlHeader, true, encoding ) ) {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing Declaration\n" );
    #endif
    returnNode = new TiXmlDeclaration();
  } else if( StringEqual( p, commentHeader, false, encoding ) ) {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing Comment\n" );
    #endif
    returnNode = new TiXmlComment();
  } else if( StringEqual( p, cdataHeader, false, encoding ) ) {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing CDATA\n" );
    #endif
    TiXmlText* text = new TiXmlText( "" );
    text->SetCDATA( true );
    returnNode = text;
  } else if( StringEqual( p, dtdHeader, false, encoding ) ) {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing Unknown(1)\n" );
    #endif
    returnNode = new TiXmlUnknown();
  } else if( IsAlpha( *( p + 1 ), encoding )
             || *( p + 1 ) == '_' ) {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing Element\n" );
    #endif
    returnNode = new TiXmlElement( "" );
  } else {
    #ifdef DEBUG_PARSER
    TIXML_LOG( "XML parsing Unknown(2)\n" );
    #endif
    returnNode = new TiXmlUnknown();
  }
  if( returnNode ) {
    // Set the parent, so it can report errors
    returnNode->parent = this;
  }
  return returnNode;
}

#ifdef TIXML_USE_STL

void TiXmlElement::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  // We're called with some amount of pre-parsing. That is, some of "this"
  // element is in "tag". Go ahead and stream to the closing ">"
  while( in->good() ) {
    int c = in->get();
    if( c <= 0 ) {
      TiXmlDocument* document = GetDocument();
      if( document ) {
        document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
      }
      return;
    }
    ( *tag ) += ( char ) c ;
    if( c == '>' ) {
      break;
    }
  }
  if( tag->length() < 3 ) {
    return;
  }
  // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
  // If not, identify and stream.
  if( tag->at( tag->length() - 1 ) == '>'
      && tag->at( tag->length() - 2 ) == '/' ) {
    // All good!
    return;
  } else if( tag->at( tag->length() - 1 ) == '>' ) {
    // There is more. Could be:
    //    text
    //    cdata text (which looks like another node)
    //    closing tag
    //    another node.
    for( ;; ) {
      StreamWhiteSpace( in, tag );
      // Do we have text?
      if( in->good() && in->peek() != '<' ) {
        // Yep, text.
        TiXmlText text( "" );
        text.StreamIn( in, tag );
        // What follows text is a closing tag or another node.
        // Go around again and figure it out.
        continue;
      }
      // We now have either a closing tag...or another node.
      // We should be at a "<", regardless.
      if( !in->good() ) {
        return;
      }
      assert( in->peek() == '<' );
      int tagIndex = ( int ) tag->length();
      bool closingTag = false;
      bool firstCharFound = false;
      for( ;; ) {
        if( !in->good() ) {
          return;
        }
        int c = in->peek();
        if( c <= 0 ) {
          TiXmlDocument* document = GetDocument();
          if( document ) {
            document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
          }
          return;
        }
        if( c == '>' ) {
          break;
        }
        *tag += ( char ) c;
        in->get();
        // Early out if we find the CDATA id.
        if( c == '[' && tag->size() >= 9 ) {
          size_t len = tag->size();
          const char* start = tag->c_str() + len - 9;
          if( strcmp( start, "<![CDATA[" ) == 0 ) {
            assert( !closingTag );
            break;
          }
        }
        if( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) {
          firstCharFound = true;
          if( c == '/' ) {
            closingTag = true;
          }
        }
      }
      // If it was a closing tag, then read in the closing '>' to clean up the input stream.
      // If it was not, the streaming will be done by the tag.
      if( closingTag ) {
        if( !in->good() ) {
          return;
        }
        int c = in->get();
        if( c <= 0 ) {
          TiXmlDocument* document = GetDocument();
          if( document ) {
            document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
          }
          return;
        }
        assert( c == '>' );
        *tag += ( char ) c;
        // We are done, once we've found our closing tag.
        return;
      } else {
        // If not a closing tag, id it, and stream.
        const char* tagloc = tag->c_str() + tagIndex;
        TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
        if( !node ) {
          return;
        }
        node->StreamIn( in, tag );
        delete node;
        node = 0;
        // No return: go around from the beginning: text, closing tag, or node.
      }
    }
  }
}
#endif

const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  p = SkipWhiteSpace( p, encoding );
  TiXmlDocument* document = GetDocument();
  if( !p || !*p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
    }
    return 0;
  }
  if( data ) {
    data->Stamp( p, encoding );
    location = data->Cursor();
  }
  if( *p != '<' ) {
    if( document ) {
      document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
    }
    return 0;
  }
  p = SkipWhiteSpace( p + 1, encoding );
  // Read the name.
  const char* pErr = p;
  p = ReadName( p, &value, encoding );
  if( !p || !*p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
    }
    return 0;
  }
  TIXML_STRING endTag( "</" );
  endTag += value;
  // Check for and read attributes. Also look for an empty
  // tag or an end tag.
  while( p && *p ) {
    pErr = p;
    p = SkipWhiteSpace( p, encoding );
    if( !p || !*p ) {
      if( document ) {
        document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
      }
      return 0;
    }
    if( *p == '/' ) {
      ++p;
      // Empty tag.
      if( *p  != '>' ) {
        if( document ) {
          document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
        }
        return 0;
      }
      return ( p + 1 );
    } else if( *p == '>' ) {
      // Done with attributes (if there were any.)
      // Read the value -- which can include other
      // elements -- read the end tag, and return.
      ++p;
      p = ReadValue( p, data, encoding );    // Note this is an Element method, and will set the error if one happens.
      if( !p || !*p ) {
        // We were looking for the end tag, but found nothing.
        // Fix for [ 1663758 ] Failure to report error on bad XML
        if( document ) {
          document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
        }
        return 0;
      }
      // We should find the end tag now
      // note that:
      // </foo > and
      // </foo>
      // are both valid end tags.
      if( StringEqual( p, endTag.c_str(), false, encoding ) ) {
        p += endTag.length();
        p = SkipWhiteSpace( p, encoding );
        if( p && *p && *p == '>' ) {
          ++p;
          return p;
        }
        if( document ) {
          document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
        }
        return 0;
      } else {
        if( document ) {
          document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
        }
        return 0;
      }
    } else {
      // Try to read an attribute:
      TiXmlAttribute* attrib = new TiXmlAttribute();
      if( !attrib ) {
        return 0;
      }
      attrib->SetDocument( document );
      pErr = p;
      p = attrib->Parse( p, data, encoding );
      if( !p || !*p ) {
        if( document ) {
          document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
        }
        delete attrib;
        return 0;
      }
      // Handle the strange case of double attributes:
      #ifdef TIXML_USE_STL
      TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
      #else
      TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
      #endif
      if( node ) {
        if( document ) {
          document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
        }
        delete attrib;
        return 0;
      }
      attributeSet.Add( attrib );
    }
  }
  return p;
}


const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  TiXmlDocument* document = GetDocument();
  // Read in text and elements in any order.
  const char* pWithWhiteSpace = p;
  p = SkipWhiteSpace( p, encoding );
  while( p && *p ) {
    if( *p != '<' ) {
      // Take what we have, make a text element.
      TiXmlText* textNode = new TiXmlText( "" );
      if( !textNode ) {
        return 0;
      }
      if( TiXmlBase::IsWhiteSpaceCondensed() ) {
        p = textNode->Parse( p, data, encoding );
      } else {
        // Special case: we want to keep the white space
        // so that leading spaces aren't removed.
        p = textNode->Parse( pWithWhiteSpace, data, encoding );
      }
      if( !textNode->Blank() ) {
        LinkEndChild( textNode );
      } else
      { delete textNode; }
    } else {
      // We hit a '<'
      // Have we hit a new element or an end tag? This could also be
      // a TiXmlText in the "CDATA" style.
      if( StringEqual( p, "</", false, encoding ) ) {
        return p;
      } else {
        TiXmlNode* node = Identify( p, encoding );
        if( node ) {
          p = node->Parse( p, data, encoding );
          LinkEndChild( node );
        } else
        { return 0; }
      }
    }
    pWithWhiteSpace = p;
    p = SkipWhiteSpace( p, encoding );
  }
  if( !p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
    }
  }
  return p;
}


#ifdef TIXML_USE_STL
void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  while( in->good() ) {
    int c = in->get();
    if( c <= 0 ) {
      TiXmlDocument* document = GetDocument();
      if( document ) {
        document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
      }
      return;
    }
    ( *tag ) += ( char ) c;
    if( c == '>' ) {
      // All is well.
      return;
    }
  }
}
#endif


const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  TiXmlDocument* document = GetDocument();
  p = SkipWhiteSpace( p, encoding );
  if( data ) {
    data->Stamp( p, encoding );
    location = data->Cursor();
  }
  if( !p || !*p || *p != '<' ) {
    if( document ) {
      document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
    }
    return 0;
  }
  ++p;
  value = "";
  while( p && *p && *p != '>' ) {
    value += *p;
    ++p;
  }
  if( !p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
    }
  }
  if( *p == '>' ) {
    return p + 1;
  }
  return p;
}

#ifdef TIXML_USE_STL
void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  while( in->good() ) {
    int c = in->get();
    if( c <= 0 ) {
      TiXmlDocument* document = GetDocument();
      if( document ) {
        document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
      }
      return;
    }
    ( *tag ) += ( char ) c;
    if( c == '>'
        && tag->at( tag->length() - 2 ) == '-'
        && tag->at( tag->length() - 3 ) == '-' ) {
      // All is well.
      return;
    }
  }
}
#endif


const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  TiXmlDocument* document = GetDocument();
  value = "";
  p = SkipWhiteSpace( p, encoding );
  if( data ) {
    data->Stamp( p, encoding );
    location = data->Cursor();
  }
  const char* startTag = "<!--";
  const char* endTag   = "-->";
  if( !StringEqual( p, startTag, false, encoding ) ) {
    document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
    return 0;
  }
  p += strlen( startTag );
  value = "";
  while( p && *p && !StringEqual( p, endTag, false, encoding ) ) {
    value.append( p, 1 );
    ++p;
  }
  if( p && *p ) {
    p += strlen( endTag );
  }
  return p;
}


const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  p = SkipWhiteSpace( p, encoding );
  if( !p || !*p ) {
    return 0;
  }
  if( data ) {
    data->Stamp( p, encoding );
    location = data->Cursor();
  }
  const char* pErr = p;
  p = ReadName( p, &name, encoding );
  if( !p || !*p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
    }
    return 0;
  }
  p = SkipWhiteSpace( p, encoding );
  if( !p || !*p || *p != '=' ) {
    if( document ) {
      document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
    }
    return 0;
  }
  ++p;
  p = SkipWhiteSpace( p, encoding );
  if( !p || !*p ) {
    if( document ) {
      document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
    }
    return 0;
  }
  const char* end;
  const char SINGLE_QUOTE = '\'';
  const char DOUBLE_QUOTE = '\"';
  if( *p == SINGLE_QUOTE ) {
    ++p;
    end = "\'";
    p = ReadText( p, &value, false, end, false, encoding );
  } else if( *p == DOUBLE_QUOTE ) {
    ++p;
    end = "\"";
    p = ReadText( p, &value, false, end, false, encoding );
  } else {
    value = "";
    while( p && *p
           && !IsWhiteSpace( *p )
           && *p != '/' && *p != '>' ) {
      if( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {
        if( document ) {
          document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
        }
        return 0;
      }
      value += *p;
      ++p;
    }
  }
  return p;
}

#ifdef TIXML_USE_STL
void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  while( in->good() ) {
    int c = in->peek();
    if( !cdata && ( c == '<' ) ) {
      return;
    }
    if( c <= 0 ) {
      TiXmlDocument* document = GetDocument();
      if( document ) {
        document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
      }
      return;
    }
    ( *tag ) += ( char ) c;
    in->get();
    if( cdata && c == '>' && tag->size() >= 3 ) {
      size_t len = tag->size();
      if( ( *tag )[len - 2] == ']' && ( *tag )[len - 3] == ']' ) {
        return;
      }
    }
  }
}
#endif

const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) {
  value = "";
  TiXmlDocument* document = GetDocument();
  if( data ) {
    data->Stamp( p, encoding );
    location = data->Cursor();
  }
  const char* const startTag = "<![CDATA[";
  const char* const endTag   = "]]>";
  if( cdata || StringEqual( p, startTag, false, encoding ) ) {
    cdata = true;
    if( !StringEqual( p, startTag, false, encoding ) ) {
      document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );
      return 0;
    }
    p += strlen( startTag );
    while( p && *p
           && !StringEqual( p, endTag, false, encoding )
         ) {
      value += *p;
      ++p;
    }
    TIXML_STRING dummy;
    p = ReadText( p, &dummy, false, endTag, false, encoding );
    return p;
  } else {
    bool ignoreWhite = true;
    const char* end = "<";
    p = ReadText( p, &value, ignoreWhite, end, false, encoding );
    if( p ) {
      return p - 1;
    }
    return 0;
  }
}

#ifdef TIXML_USE_STL
void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) {
  while( in->good() ) {
    int c = in->get();
    if( c <= 0 ) {
      TiXmlDocument* document = GetDocument();
      if( document ) {
        document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
      }
      return;
    }
    ( *tag ) += ( char ) c;
    if( c == '>' ) {
      return;
    }
  }
}
#endif

const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) {
  p = SkipWhiteSpace( p, _encoding );
  TiXmlDocument* document = GetDocument();
  if( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) {
    if( document ) {
      document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
    }
    return 0;
  }
  if( data ) {
    data->Stamp( p, _encoding );
    location = data->Cursor();
  }
  p += 5;
  version = "";
  encoding = "";
  standalone = "";
  while( p && *p ) {
    if( *p == '>' ) {
      ++p;
      return p;
    }
    p = SkipWhiteSpace( p, _encoding );
    if( StringEqual( p, "version", true, _encoding ) ) {
      TiXmlAttribute attrib;
      p = attrib.Parse( p, data, _encoding );
      version = attrib.Value();
    } else if( StringEqual( p, "encoding", true, _encoding ) ) {
      TiXmlAttribute attrib;
      p = attrib.Parse( p, data, _encoding );
      encoding = attrib.Value();
    } else if( StringEqual( p, "standalone", true, _encoding ) ) {
      TiXmlAttribute attrib;
      p = attrib.Parse( p, data, _encoding );
      standalone = attrib.Value();
    } else {
      while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) {
        ++p;
      }
    }
  }
  return 0;
}

bool TiXmlText::Blank() const {
  for( unsigned i = 0; i < value.length(); i++ )
    if( !IsWhiteSpace( value[i] ) ) {
      return false;
    }
  return true;
}

